package com.erikpasternak.vader;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.app.Fragment;
import android.content.SharedPreferences;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

public class VideoFragment extends Fragment implements View.OnClickListener,
        MediaScannerConnectionClient {
    private static final String TAG = "VideoFragment";

    private static final boolean SKIP_RECORDING = false;

    private static final int RECORDING_TIME = 30 * 1000;
    private VaderActivity.VaderStateListener mListener;
    private Camera mCamera;
    private int mCameraId;
    private SurfaceView mSurfaceView;
    private MediaScannerConnection mMediaScanner;
    private boolean mMediaScannerConnected = false;
    private MediaRecorder mRecorder = new MediaRecorder();
    private File mPath;
    private File mOutputFile;
    private final Handler mHandler = new Handler();

    private boolean mResumed = false;
    private boolean mRecorded = false;

    private long mStartTime = 0;

    private final Runnable mUpdateTitleRunnable = new Runnable() {

        public void run() {
            int remaining = (int)
                    ((RECORDING_TIME - (SystemClock.elapsedRealtime() - mStartTime)) / 1000) + 1;
            if (remaining < 1) {
                return;
            }
            // Make the next time 30ms further out to ensure the count always
            // moves to the next second.
            long nextTime = 1000 - ((SystemClock.elapsedRealtime() - mStartTime) % 1000) + 30;
            mTitle.setText("Time remaining: " + remaining + " seconds");
            mHandler.postDelayed(this, nextTime);
        }
    };

    private final Runnable mCameraReadyRunnable = new Runnable() {
        public void run() {
            if (mCamera == null || !mResumed) {
                Log.d(VaderActivity.TAG, "mCamera is " + mCamera + " resumed? " + mResumed);
                return;
            }
            try {
                mCamera.setPreviewDisplay(mSurfaceView.getHolder());
                mCamera.startPreview();
                mNext.setEnabled(true);
            } catch (IOException e) {
                Log.e(VaderActivity.TAG, "Error displaying preview", e);
            }
        }
    };

    private final Runnable mGetCameraRunnable = new Runnable() {
        public void run() {
            setUpCamera();
        }
    };
    private SharedPreferences mPrefs;
    private int mVideosCount;
    private Button mNext;

    private TextView mTitle;

    private Runnable mStopRecordingRunnable;

    private void setUpCamera() {
        boolean cameraFound = false;
        CameraInfo cameraInfo = new CameraInfo();
        for (int i = Camera.getNumberOfCameras() - 1; i >= 0; i--) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo != null && cameraInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
                mCameraId = i;
                cameraFound = true;
                break;
            }
        }
        if (!cameraFound) {
            Log.e(VaderActivity.TAG, "No front facing camera found! Restarting");
            mListener.onStateChange(VaderActivity.STATE_INITIAL);
            return;
        }

        boolean error = false;
        if (mCamera != null) {
            try {
                mCamera.reconnect();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else {
            try {
                mCamera = Camera.open(mCameraId);
            } catch (Exception e) {
                Log.w(VaderActivity.TAG, "Failed to get camera", e);
                error = true;
            }
        }

        Log.d(VaderActivity.TAG, "Camera returned is " + mCamera);
        if (mCamera == null || error) {
            mHandler.removeCallbacks(mGetCameraRunnable);
            mHandler.postDelayed(mGetCameraRunnable, 5000);
        } else {
            mHandler.post(mCameraReadyRunnable);
        }
        return;
    }

    public VideoFragment() {
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mListener = ((VaderActivity)activity).getListener();
        mPrefs = activity.getSharedPreferences("BenderPrefs", 0);
        mMediaScanner = new MediaScannerConnection(activity, this);
        mVideosCount = mPrefs.getInt("video_count", 0);
        mPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
        mPath = new File(mPath.getAbsolutePath() + "/Camera");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle bundle) {
        View view = inflater.inflate(R.layout.video, parent, false);
        mSurfaceView = (SurfaceView) view.findViewById(R.id.preview);
        mTitle = (TextView) view.findViewById(R.id.title);
        view.findViewById(R.id.cancel).setOnClickListener(this);
        view.findViewById(R.id.next).setOnClickListener(this);
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();

        mHandler.post(mGetCameraRunnable);
        mMediaScanner.connect();
        mNext = (Button) getView().findViewById(R.id.next);
        mNext.setEnabled(false);
        mResumed = true;
    }

    @Override
    public void onPause() {
        super.onPause();
        mHandler.removeCallbacks(mGetCameraRunnable);

        mMediaScanner.disconnect();
        if (mRecorder != null) {
            try {
                mRecorder.stop();
                mRecorder.release();
            } catch (IllegalStateException e) {
                Log.d(TAG, "stupid fw ", e);
            } catch (RuntimeException e) {
                // This happens if cancel is pressed right after start recording
            }
            mRecorder = null;

            mNext.setEnabled(true);
            mNext.setText("Record");
        }
        if (mStopRecordingRunnable != null) {
            mHandler.removeCallbacks(mStopRecordingRunnable);
        }

        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        mResumed = false;
    }

    public void onClick(final View v) {
        if (v.getId() == R.id.cancel) {
            mListener.onStateChange(VaderActivity.STATE_INITIAL);
        } else if (SKIP_RECORDING) {
            mListener.onStateChange(VaderActivity.STATE_DRINK);
        } else if (!mRecorded) {
            mNext.setEnabled(false);
            mNext.setText("Recording...");
            mStartTime = SystemClock.elapsedRealtime();
            mHandler.post(mUpdateTitleRunnable);

            mCamera.unlock();
            mRecorder.setCamera(mCamera);
            mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
            mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
            CamcorderProfile profile = CamcorderProfile
                    .get(mCameraId, CamcorderProfile.QUALITY_HIGH);
            // mRecorder.setProfile(profile);
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mOutputFile = new File(mPath, "Bender " + mVideosCount + ".mp4");
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(mOutputFile);
            } catch (FileNotFoundException e1) {
                Log.d(TAG, "*******OH NOES", e1);
            }
            Log.d(TAG, "Recording to " + mOutputFile.getAbsolutePath());
            try {
                if (fos != null)
                    mRecorder.setOutputFile(fos.getFD());
            } catch (IllegalStateException e1) {
                Log.d(TAG, "*******OH NOES", e1);
            } catch (IOException e1) {
                Log.d(TAG, "*******OH NOES", e1);
            }
            mRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface());
            try {
                mRecorder.prepare();
                mRecorder.start();
                mStopRecordingRunnable = new Runnable() {

                    public void run() {
                        if (mRecorder != null) {
                            mRecorder.stop();
                            mRecorder.release();
                            mRecorder = null;
                            mRecorded = true;
                            if (mMediaScannerConnected) {
                                Log.d(TAG, "Scanning!");
                                mMediaScanner.scanFile(mOutputFile.getAbsolutePath(), null);
                            }

                            mNext.setEnabled(true);
                            mNext.setText("Next");
                            mTitle.setText("Message recorded!");
                            mVideosCount++;
                            mPrefs.edit().putInt("video_count", mVideosCount).apply();
                            mStopRecordingRunnable = null;
                        }
                    }

                };
                mHandler.postDelayed(mStopRecordingRunnable, RECORDING_TIME);
            } catch (IllegalStateException e) {
                Log.d(TAG, "*******OH NOES", e);
                if (mRecorder != null) {
                    mRecorder.release();
                }
            } catch (IOException e) {
                Log.d(TAG, "*******OH NOES", e);
                if (mRecorder != null) {
                    mRecorder.release();
                }
            }
        } else {
            mListener.onStateChange(VaderActivity.STATE_DRINK);
        }
    }

    public void onMediaScannerConnected() {
        mMediaScannerConnected = true;
    }

    public void onScanCompleted(String path, Uri uri) {
    }

}
